#ifndef __TCP_SERVER_HPP__
#define __TCP_SERVER_HPP__

#include <iostream>
#include <unordered_map>
#include <functional>
#include <cassert>
#include <fcntl.h>
#include "Sock.hpp"
#include "Epoll.hpp"
#include "Protocol.hpp"

using namespace ns_sock;
using namespace ns_protocol;

namespace ns_server
{
    // 声明
    class TcpServer;
    class Connection;

    using func_t = std::function<void(Connection *)>;
    using callback_t = std::function<void(Connection *, std::string)>;

    class Connection
    {
    public:
        // socket fd
        int _sockfd;

        // 回调方法
        func_t _recv_cb;
        func_t _send_cb;
        func_t _except_cb;

        // 读取与写入缓冲区
        std::string _in_buffer;
        std::string _out_buffer;

        // 指向TcpSever
        TcpServer *_tcp_server_ptr;

    public:
        Connection(int sockfd = -1, TcpServer *tcp_server_ptr = nullptr)
            : _sockfd(sockfd), _tcp_server_ptr(tcp_server_ptr)
        {
        }

        ~Connection()
        {
            if (_sockfd >= 0)
            {
                close(_sockfd);
            }
        }

        void SetCallBack(func_t recv_cb, func_t send_cb, func_t except_cb)
        {
            _recv_cb = recv_cb;
            _send_cb = send_cb;
            _except_cb = except_cb;
        }
    };

    class TcpServer
    {
        static const uint16_t gport = 8080;
        static const int grevs_num = 99;

    private:
        int _listen_sock; // 监听套接字
        uint16_t _port;   // 服务器绑定的端口

        Epoll _epoll; // epoll模型

        std::unordered_map<int, Connection *> _connections; // 文件描述符与Connection*的映射

        struct epoll_event *_revs_ptr; // 就绪事件的集合
        int _revs_num;

        callback_t _cb;

    public:
        TcpServer(uint16_t port = gport, int revs_num = grevs_num) : _port(port), _revs_num(revs_num)
        {
            // 1.创建监听套接字
            _listen_sock = Sock::Socket();
            Sock::Bind(_listen_sock, _port);
            Sock::Listen(_listen_sock);

            // 2.创建epoll模型
            _epoll.CreateEpoll();

            // 3.将监听套接字加入到epoll模型中
            AddConnection(_listen_sock, std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);

            // 4. 构建一个获取就绪事件的缓冲区
            _revs_ptr = new struct epoll_event[_revs_num];
        }

        void AddConnection(int sockfd, func_t recv_cb, func_t send_cb, func_t except_cb)
        {
            // 0.SetNonBlock
            Sock::SetNonBlock(sockfd);
            // 1.CreateConnection
            Connection *conn_ptr = new Connection(sockfd, this);
            conn_ptr->SetCallBack(recv_cb, send_cb, except_cb);
            // 2.AddEpoll
            // 这里无需注册EPOLLOUT事件,输出事件由我们自己根据需求而定,因为一开始发送缓冲区为空,
            // 如果一开始就注册EPOLLOUT,则对应的sockfd就会一种就绪没有意义
            _epoll.AddSockToEpoll(sockfd, EPOLLIN | EPOLLET);
            // 3.SockfdHashConnectionPtr
            _connections.insert({sockfd, conn_ptr});
        }

        void EnableReadWrite(Connection *conn_ptr, bool readop, bool writeop)
        {
            uint32_t events = (readop ? EPOLLIN : 0) | (writeop ? EPOLLOUT : 0) | EPOLLET;

            assert(_epoll.ModSockEvents(conn_ptr->_sockfd, events));
        }

        bool IsConnectionExists(int sockfd)
        {
            auto iter = _connections.find(sockfd);
            if (iter == _connections.end())
                return false;
            else
                return true;
        }

        void Accepter(Connection *conn_ptr)
        {
            // std::cout << "Call Accepter" << std::endl;

            while (true) // EPOLLET模式,边沿触发,一旦有事件就绪则将本次就绪的数据一次读取完
            {
                std::string client_ip;
                uint16_t client_port;
                int accept_errno = 0;

                // 调用accept可以直接从全连接队列中获取sockfd,因为listensock读事件已经就绪
                int sockfd = Sock::Accept(_listen_sock, &client_port, &client_ip, &accept_errno);
                if (sockfd < 0)
                {
                    if (accept_errno == EAGAIN || accept_errno == EWOULDBLOCK) // 非阻塞式返回
                    {
                        break;
                    }
                    else if (accept_errno == EINTR) // 因为信号中断而返回
                    {
                        continue;
                    }
                    else // accept出错返回
                    {
                        std::cerr << "errno: " << accept_errno << "strerr: " << strerror(accept_errno) << std::endl;
                        break;
                    }
                }

                if (sockfd >= 0) // 将获取到的sockfd进行组织管理
                {
                    AddConnection(sockfd, std::bind(&TcpServer::Recver, this, std::placeholders::_1),
                                  std::bind(&TcpServer::Sender, this, std::placeholders::_1),
                                  std::bind(&TcpServer::Sender, this, std::placeholders::_1));
                    printf("clientip: %s clientport: %d sockfd: %d\n", client_ip.c_str(), client_port, sockfd);
                }
            }
        }

        void Recver(Connection *conn_ptr)
        {
            char buffer[1024];
            bool flag = true;
            while (true)
            {
                ssize_t n = recv(conn_ptr->_sockfd, buffer, sizeof(buffer) - 1, 0); // 非阻塞式读取
                if (n < 0)
                {
                    if (errno == EAGAIN || errno == EWOULDBLOCK)
                    {
                        break;
                    }
                    else if (errno == EINTR)
                    {
                        continue;
                    }
                    else
                    {
                        flag = false;
                        std::cerr << "errno: " << errno << "strerr: " << strerror(errno) << std::endl;
                        if (conn_ptr->_except_cb != nullptr) // 如果recv出错则调用对应的异常处理事件
                        {
                            conn_ptr->_except_cb(conn_ptr);
                            return;
                        }
                        break;
                    }
                }
                else if (0 == n)
                {
                    flag = false;
                    std::cout << "client close" << std::endl;
                    if (conn_ptr->_except_cb != nullptr)
                    {
                        conn_ptr->_except_cb(conn_ptr);
                        return;
                    }
                    break;
                }
                else
                {
                    buffer[n] = '\0';
                    conn_ptr->_in_buffer += buffer; // 将读取到的数据存放到sockfd对应的缓冲区中

                    // std::cout<<"sockfd: "<<conn_ptr<<" inbuffer: "<<conn_ptr->_in_buffer<<std::endl;
                }
            }

            std::cout << "sockfd: " << conn_ptr << " inbuffer: " << conn_ptr->_in_buffer << std::endl;
            if (flag)
            {
                std::vector<std::string> requests;
                Splite(conn_ptr->_in_buffer, &requests);
                for (auto &str : requests)
                {
                    _cb(conn_ptr, str);
                }
            }
        }

        void Sender(Connection *conn_ptr)
        {
            while (true)
            {
                ssize_t n = send(conn_ptr->_sockfd, conn_ptr->_out_buffer.c_str(), conn_ptr->_out_buffer.size(), 0);
                if (n < 0)
                {
                    if (errno == EAGAIN || errno == EWOULDBLOCK) // 非阻塞式返回
                    {
                        break;
                    }
                    else if (errno == EINTR) // 因信号中断返回
                    {
                        continue;
                    }
                    else // send错误
                    {
                        printf("send errno: %d strerr: %s sockfd %d\n",errno,strerror(errno),conn_ptr->_sockfd);
                        if(conn_ptr!=nullptr && conn_ptr->_except_cb!=nullptr)
                        {
                            conn_ptr->_except_cb(conn_ptr);
                            return;
                        }
                        break;
                    }
                }
                else if (0 == n)
                {
                    // 不能因为读端关闭而导致进程退出,需要将SIGPIPE设置捕获为SIG_IGN
                    // signal(SIGPIPE,SIG_IGN)
                }
                else
                {
                    // 将已经发送的数据删除掉
                    conn_ptr->_out_buffer.erase(0,n);
                    if(conn_ptr->_out_buffer.empty())
                        break;
                }
            }

            if(conn_ptr->_out_buffer.empty()) {
                EnableReadWrite(conn_ptr,true,false); // 如果sockfd的outbuffer发送完成则关闭EPOLLOUT
            } else {
                EnableReadWrite(conn_ptr,true,true);
            }

        }

        void Excepter(Connection *conn_ptr)
        {
            if(!IsConnectionExists(conn_ptr->_sockfd)) return;
            // 1.将sockfd从epoll模型中删除
            assert(_epoll.DelFromEpoll(conn_ptr->_sockfd));
            // 2.从connections中删除
            _connections.erase(conn_ptr->_sockfd);
            // 3.关闭sockfd
            close(conn_ptr->_sockfd);
            // 4.delete
            delete conn_ptr;
        }

        void LoopOnce()
        {
            int n = _epoll.WaitEpoll(_revs_ptr, _revs_num);

            for (int i = 0; i < n; ++i)
            {
                int sockfd = _revs_ptr[i].data.fd;
                uint32_t events = _revs_ptr[i].events;

                // 读事件就绪
                if (events & EPOLLIN)
                {
                    if (IsConnectionExists(sockfd) && _connections[sockfd]->_recv_cb != nullptr)
                    {
                        _connections[sockfd]->_recv_cb(_connections[sockfd]);
                    }
                }

                // 写事件就绪
                if (events & EPOLLOUT)
                {
                    if (IsConnectionExists(sockfd) && _connections[sockfd]->_send_cb != nullptr)
                    {
                        _connections[sockfd]->_send_cb(_connections[sockfd]);
                    }
                }
            }
        }

        void Dispatch(callback_t cb)
        {
            _cb = cb;
            while (true)
            {
                LoopOnce();
            }
        }

        ~TcpServer()
        {
            if (_listen_sock >= 0)
            {
                close(_listen_sock);
            }
            if (_revs_ptr != nullptr)
            {
                delete[] _revs_ptr;
            }
        }
    };
}

#endif